﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using IWshRuntimeLibrary;
using BoxSharp;

namespace BoxSharpSmart
{
    public class BoxSharpSuggestItem
    {
        public string ItemName;
        public string ItemGroup;
        public string ItemDescription;
        public System.Diagnostics.ProcessStartInfo ItemStarter;
    }

    public class BoxSharpSmart : AddonInstance
    {
        private AddonInterface myInterface;
        private WshShell myShell;
        private ToolTip tip;
        private StackPanel panelSuggests;
        private Dictionary<string, List<BoxSharpSuggestItem>> suggests = new Dictionary<string, List<BoxSharpSuggestItem>>();
        private int currSelectedSuggest = -1;

        public override void AddonMain(AddonInterface addonInterface)
        {
            myInterface = addonInterface;

            myInterface.AddonInitializing += new AddonEventHandler(myInterface_AddonInitializing);
            myInterface.AddonFinalizing += new AddonEventHandler(myInterface_AddonFinalizing);
        }

        void myInterface_AddonFinalizing()
        {
        }

        void myInterface_AddonInitializing()
        {
            myShell = new WshShell();

            tip = new ToolTip();
            tip.HasDropShadow = true;
            tip.PlacementTarget = myInterface.MainWindow;
            tip.Placement = System.Windows.Controls.Primitives.PlacementMode.Bottom;
            tip.IsOpen = false;
            tip.Background = System.Windows.Media.Brushes.White;
            tip.VerticalOffset = 5;
            tip.BorderBrush = System.Windows.Media.Brushes.Gray;
            tip.Padding = new Thickness(0);

            panelSuggests = new StackPanel();
            panelSuggests.Background = System.Windows.Media.Brushes.White;

            tip.Content = panelSuggests;

            myInterface.Box.TextChanged += new TextChangedEventHandler(Box_TextChanged);
            myInterface.Box.PreviewKeyDown += new System.Windows.Input.KeyEventHandler(Box_PreviewKeyDown);
            myInterface.OnCommand += new CommandEventHandler(myInterface_OnCommand);

            IndexFromPrograms();
            IndexFromFavorites();
        }

        void myInterface_OnCommand(string bscommand)
        {
            if (bscommand.StartsWith("/")) return;

            if (currSelectedSuggest >= 0)
            {
                StackPanel currItem = panelSuggests.Children[currSelectedSuggest] as StackPanel;
                BoxSharpSuggestItem currSuggest = currItem.Tag as BoxSharpSuggestItem;
                try
                {
                    System.Diagnostics.Process.Start(currSuggest.ItemStarter);
                }
                catch (System.Exception ex)
                {
                    MessageBox.Show("Failed to run \"" + currSuggest.ItemName + "\". Here's the error message.\r\n" + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                }

                myInterface.Box.Text = "";
                tip.IsOpen = false;
            }
        }

        void MoveSuggestUp()
        {
            StackPanel prevItem = panelSuggests.Children[currSelectedSuggest] as StackPanel;
            prevItem.Background = System.Windows.Media.Brushes.Transparent;

            if (currSelectedSuggest == 0)
            {
                currSelectedSuggest = panelSuggests.Children.Count - 1;
            }
            else
            {
                currSelectedSuggest--;
            }

            StackPanel currItem = panelSuggests.Children[currSelectedSuggest] as StackPanel;
            if (currItem.Tag == null)
            {
                MoveSuggestUp();
            }
            else
            {
                currItem.Background = System.Windows.Media.Brushes.AliceBlue;
            }
        }

        void MoveSuggestDown()
        {
            StackPanel prevItem = panelSuggests.Children[currSelectedSuggest] as StackPanel;
            prevItem.Background = System.Windows.Media.Brushes.Transparent;

            if (currSelectedSuggest == panelSuggests.Children.Count - 1)
            {
                currSelectedSuggest = 0;
            }
            else
            {
                currSelectedSuggest++;
            }

            StackPanel currItem = panelSuggests.Children[currSelectedSuggest] as StackPanel;
            if (currItem.Tag == null)
            {
                MoveSuggestDown();
            }
            else
            {
                currItem.Background = System.Windows.Media.Brushes.AliceBlue;
            }
        }

        void Box_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
        {
            if (e.Key == System.Windows.Input.Key.Up)
            {
                if (currSelectedSuggest >= 0 && tip.IsOpen)
                {
                    MoveSuggestUp();

                    e.Handled = true;
                }
            }
            else if (e.Key == System.Windows.Input.Key.Down)
            {
                if (currSelectedSuggest >= 0 && tip.IsOpen)
                {
                    MoveSuggestDown();

                    e.Handled = true;
                }
            }
        }

        void Box_TextChanged(object sender, TextChangedEventArgs e)
        {
            string command = myInterface.Box.Text;

            if (command.StartsWith("/")) return;

            if (suggests.ContainsKey(command.ToLower()))
            {
                panelSuggests.Children.Clear();

                StackPanel highLightItem = null;

                List<string> groups = new List<string>();

                foreach (BoxSharpSuggestItem item in suggests[command])
                {
                    if (!groups.Contains(item.ItemGroup))
                    {
                        groups.Add(item.ItemGroup);
                    }
                }

                foreach (string group in groups)
                {
                    StackPanel panelGroup = new StackPanel();
                    panelGroup.Orientation = Orientation.Horizontal;

                    TextBlock textGroupName = new TextBlock();
                    textGroupName.Foreground = System.Windows.Media.Brushes.Gray;
                    textGroupName.FontSize = 13;
                    textGroupName.Text = group;
                    textGroupName.Margin = new Thickness(10, 5, 5, 5);
                    textGroupName.VerticalAlignment = VerticalAlignment.Center;

                    StackPanel panelSep = new StackPanel();
                    panelSep.MinWidth = 400;
                    panelSep.VerticalAlignment = VerticalAlignment.Center;

                    Separator sep = new Separator();
                    sep.BorderBrush = System.Windows.Media.Brushes.LightGray;
                    sep.Background = System.Windows.Media.Brushes.LightGray;
                    sep.Foreground = System.Windows.Media.Brushes.LightGray;
                    sep.VerticalAlignment = VerticalAlignment.Center;

                    panelSep.Children.Add(sep);

                    panelGroup.Children.Add(textGroupName);
                    panelGroup.Children.Add(panelSep);

                    panelSuggests.Children.Add(panelGroup);

                    foreach (BoxSharpSuggestItem item in suggests[command])
                    {
                        if (item.ItemGroup != group) continue;

                        StackPanel panelItem = new StackPanel();
                        panelItem.Orientation = Orientation.Horizontal;
                        panelItem.Tag = item;

                        TextBlock textItemName = new TextBlock();
                        textItemName.Text = item.ItemName;
                        textItemName.FontSize = 13;
                        textItemName.Width = 300;
                        textItemName.Margin = new Thickness(20, 5, 20, 5);

                        TextBlock textItemDescription = new TextBlock();
                        textItemDescription.Text = item.ItemDescription;
                        textItemDescription.TextAlignment = TextAlignment.Right;
                        textItemDescription.Foreground = System.Windows.Media.Brushes.Gray;
                        textItemDescription.Margin = new Thickness(5);
                        textItemDescription.VerticalAlignment = VerticalAlignment.Center;

                        panelItem.Children.Add(textItemName);
                        panelItem.Children.Add(textItemDescription);

                        panelSuggests.Children.Add(panelItem);

                        if (highLightItem == null)
                        {
                            currSelectedSuggest = panelSuggests.Children.Count - 1;
                            highLightItem = panelItem;
                        }
                    }
                }

                highLightItem.Background = System.Windows.Media.Brushes.AliceBlue;

                tip.IsOpen = true;
            }
            else
            {
                currSelectedSuggest = -1;
                tip.IsOpen = false;
            }
        }

        void IndexFromProgramFiles()
        {
            string programFiles = System.Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);

            IndexProgramFilesFromFolder(programFiles);
        }

        void IndexProgramFilesFromFolder(string folder)
        {
            DirectoryInfo folderDir = new DirectoryInfo(folder);
            DirectoryInfo[] subFolders = folderDir.GetDirectories();

            foreach (DirectoryInfo subDir in subFolders)
            {
                IndexProgramFilesFromFolder(subDir.FullName);
            }

            FileInfo[] linkFiles = folderDir.GetFiles("*.exe");

            foreach (FileInfo file in linkFiles)
            {
                string fileName = Path.GetFileNameWithoutExtension(file.Name);

                BoxSharpSuggestItem suggest = new BoxSharpSuggestItem();
                suggest.ItemGroup = "Application";
                suggest.ItemName = fileName + ".exe";
                suggest.ItemDescription = file.FullName;
                suggest.ItemStarter = new System.Diagnostics.ProcessStartInfo();
                suggest.ItemStarter.FileName = file.FullName;

                string[] fileNameWords = fileName.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);

                string nameForShort = "";
                bool shortNameComplete = false;

                foreach (string fileNameWord in fileNameWords)
                {
                    if (!shortNameComplete && (fileNameWord[0] < '0' || fileNameWord[0] > '9'))
                    {
                        nameForShort += fileNameWord[0];
                    }
                    else
                    {
                        shortNameComplete = true;
                    }

                    if (!suggests.ContainsKey(fileNameWord.ToLower()))
                    {
                        suggests.Add(fileNameWord.ToLower(), new List<BoxSharpSuggestItem>());
                    }

                    bool alreadyContain = false;
                    foreach (BoxSharpSuggestItem item in suggests[fileNameWord.ToLower()])
                    {
                        if (item.ItemStarter.Arguments == suggest.ItemStarter.Arguments && item.ItemStarter.FileName == suggest.ItemStarter.FileName && item.ItemStarter.WorkingDirectory == suggest.ItemStarter.WorkingDirectory)
                        {
                            alreadyContain = true;
                            break;
                        }
                    }

                    if (!alreadyContain)
                    {
                        suggests[fileNameWord.ToLower()].Add(suggest);
                    }
                }

                for (int j = 0; j < fileNameWords.Length; ++j)
                {
                    for (int k = j + 1; k < fileNameWords.Length; ++k)
                    {
                        string fileNameWord = "";
                        for (int s = j; s <= k; ++s)
                        {
                            fileNameWord += fileNameWords[s] + " ";
                        }

                        fileNameWord = fileNameWord.Substring(0, fileNameWord.Length - 1);

                        if (!suggests.ContainsKey(fileNameWord.ToLower()))
                        {
                            suggests.Add(fileNameWord.ToLower(), new List<BoxSharpSuggestItem>());
                        }

                        bool alreadyContain = false;
                        foreach (BoxSharpSuggestItem item in suggests[fileNameWord.ToLower()])
                        {
                            if (item.ItemStarter.Arguments == suggest.ItemStarter.Arguments && item.ItemStarter.FileName == suggest.ItemStarter.FileName && item.ItemStarter.WorkingDirectory == suggest.ItemStarter.WorkingDirectory)
                            {
                                alreadyContain = true;
                                break;
                            }
                        }

                        if (!alreadyContain)
                        {
                            suggests[fileNameWord.ToLower()].Add(suggest);
                        }
                    }
                }

                if (nameForShort.Length > 1)
                {
                    if (!suggests.ContainsKey(nameForShort.ToLower()))
                    {
                        suggests.Add(nameForShort.ToLower(), new List<BoxSharpSuggestItem>());
                    }

                    bool alreadyContain = false;
                    foreach (BoxSharpSuggestItem item in suggests[nameForShort.ToLower()])
                    {
                        if (item.ItemStarter.Arguments == suggest.ItemStarter.Arguments && item.ItemStarter.FileName == suggest.ItemStarter.FileName && item.ItemStarter.WorkingDirectory == suggest.ItemStarter.WorkingDirectory)
                        {
                            alreadyContain = true;
                            break;
                        }
                    }

                    if (!alreadyContain)
                    {
                        suggests[nameForShort.ToLower()].Add(suggest);
                    }
                }
            }
        }

        void IndexFromFavorites()
        {
            string favorites = System.Environment.GetFolderPath(Environment.SpecialFolder.Favorites);

            IndexFavoritesFromFolder(favorites);
        }

        void IndexFavoritesFromFolder(string folder)
        {
            DirectoryInfo folderDir = new DirectoryInfo(folder);
            DirectoryInfo[] subFolders = folderDir.GetDirectories();

            foreach (DirectoryInfo subDir in subFolders)
            {
                IndexFavoritesFromFolder(subDir.FullName);
            }

            FileInfo[] linkFiles = folderDir.GetFiles("*.url");

            foreach (FileInfo file in linkFiles)
            {
                string fileName = Path.GetFileNameWithoutExtension(file.Name);

                BoxSharpSuggestItem suggest = new BoxSharpSuggestItem();
                suggest.ItemGroup = "Internet";
                suggest.ItemName = fileName;

                StreamReader fileReader = file.OpenText();

                while (fileReader.Peek() > -1)
                {
                    string line = fileReader.ReadLine();

                    if (line.ToLower().StartsWith("url="))
                    {
                        suggest.ItemDescription = line.Substring(4);
                        suggest.ItemStarter = new System.Diagnostics.ProcessStartInfo();
                        suggest.ItemStarter.FileName = suggest.ItemDescription;
                    }
                }
                fileReader.Close();
                fileReader.Dispose();

                string[] fileNameWords = fileName.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);

                string nameForShort = "";
                bool shortNameComplete = false;

                foreach (string fileNameWord in fileNameWords)
                {
                    if (!shortNameComplete && (fileNameWord[0] < '0' || fileNameWord[0] > '9'))
                    {
                        nameForShort += fileNameWord[0];
                    }
                    else
                    {
                        shortNameComplete = true;
                    }

                    if (!suggests.ContainsKey(fileNameWord.ToLower()))
                    {
                        suggests.Add(fileNameWord.ToLower(), new List<BoxSharpSuggestItem>());
                    }

                    bool alreadyContain = false;
                    foreach (BoxSharpSuggestItem item in suggests[fileNameWord.ToLower()])
                    {
                        if (item.ItemStarter.Arguments == suggest.ItemStarter.Arguments && item.ItemStarter.FileName == suggest.ItemStarter.FileName && item.ItemStarter.WorkingDirectory == suggest.ItemStarter.WorkingDirectory)
                        {
                            alreadyContain = true;
                            break;
                        }
                    }

                    if (!alreadyContain)
                    {
                        suggests[fileNameWord.ToLower()].Add(suggest);
                    }
                }

                for (int j = 0; j < fileNameWords.Length; ++j)
                {
                    for (int k = j + 1; k < fileNameWords.Length; ++k)
                    {
                        string fileNameWord = "";
                        for (int s = j; s <= k; ++s)
                        {
                            fileNameWord += fileNameWords[s] + " ";
                        }

                        fileNameWord = fileNameWord.Substring(0, fileNameWord.Length - 1);

                        if (!suggests.ContainsKey(fileNameWord.ToLower()))
                        {
                            suggests.Add(fileNameWord.ToLower(), new List<BoxSharpSuggestItem>());
                        }

                        bool alreadyContain = false;
                        foreach (BoxSharpSuggestItem item in suggests[fileNameWord.ToLower()])
                        {
                            if (item.ItemStarter.Arguments == suggest.ItemStarter.Arguments && item.ItemStarter.FileName == suggest.ItemStarter.FileName && item.ItemStarter.WorkingDirectory == suggest.ItemStarter.WorkingDirectory)
                            {
                                alreadyContain = true;
                                break;
                            }
                        }

                        if (!alreadyContain)
                        {
                            suggests[fileNameWord.ToLower()].Add(suggest);
                        }
                    }
                }

                if (nameForShort.Length > 1)
                {
                    if (!suggests.ContainsKey(nameForShort.ToLower()))
                    {
                        suggests.Add(nameForShort.ToLower(), new List<BoxSharpSuggestItem>());
                    }

                    bool alreadyContain = false;
                    foreach (BoxSharpSuggestItem item in suggests[nameForShort.ToLower()])
                    {
                        if (item.ItemStarter.Arguments == suggest.ItemStarter.Arguments && item.ItemStarter.FileName == suggest.ItemStarter.FileName && item.ItemStarter.WorkingDirectory == suggest.ItemStarter.WorkingDirectory)
                        {
                            alreadyContain = true;
                            break;
                        }
                    }

                    if (!alreadyContain)
                    {
                        suggests[nameForShort.ToLower()].Add(suggest);
                    }
                }

                string urlName = suggest.ItemStarter.FileName.Substring(7).ToLower();

                if (urlName.Length > 1)
                {
                    string[] urlSegments = urlName.Split(new string[] { "/" }, StringSplitOptions.RemoveEmptyEntries);

                    string domain = urlSegments[0];

                    string[] domainSegments = domain.Split(new string[] { "." }, StringSplitOptions.RemoveEmptyEntries);

                    foreach (string exeName in domainSegments)
                    {
                        if (!suggests.ContainsKey(exeName.ToLower()))
                        {
                            suggests.Add(exeName.ToLower(), new List<BoxSharpSuggestItem>());
                        }

                        bool alreadyContain = false;
                        foreach (BoxSharpSuggestItem item in suggests[exeName.ToLower()])
                        {
                            if (item.ItemStarter.Arguments == suggest.ItemStarter.Arguments && item.ItemStarter.FileName == suggest.ItemStarter.FileName && item.ItemStarter.WorkingDirectory == suggest.ItemStarter.WorkingDirectory)
                            {
                                alreadyContain = true;
                                break;
                            }
                        }

                        if (!alreadyContain)
                        {
                            suggests[exeName.ToLower()].Add(suggest);
                        }
                    }
                }
            }
        }

        void IndexFromPrograms()
        {
            string userPrograms = System.Environment.GetFolderPath(Environment.SpecialFolder.Programs);
            string desktop = System.Environment.GetFolderPath(Environment.SpecialFolder.Desktop);

            string commonPrograms = null;
            try
            {
                commonPrograms = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("Software").OpenSubKey("Microsoft").OpenSubKey("Windows").OpenSubKey("CurrentVersion").OpenSubKey("Explorer").OpenSubKey("Shell Folders").GetValue("Common Programs").ToString();
            }
            catch (System.Exception)
            {
            }

            string commonDesktop = null;
            try
            {
                commonDesktop = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("Software").OpenSubKey("Microsoft").OpenSubKey("Windows").OpenSubKey("CurrentVersion").OpenSubKey("Explorer").OpenSubKey("Shell Folders").GetValue("Common Desktop").ToString();
            }
            catch (System.Exception)
            {
            }
            
            IndexShortCutsFromFolder(userPrograms);
            IndexShortCutsFromFolder(desktop);

            if (commonPrograms != null && commonPrograms.Length > 0)
            {
                IndexShortCutsFromFolder(commonPrograms);
            }

            if (commonDesktop != null && commonDesktop.Length > 0)
            {
                IndexShortCutsFromFolder(commonDesktop);
            }
        }

        void IndexShortCutsFromFolder(string folder)
        {
            DirectoryInfo folderDir = new DirectoryInfo(folder);
            DirectoryInfo[] subFolders = folderDir.GetDirectories();

            foreach (DirectoryInfo subDir in subFolders)
            {
                IndexShortCutsFromFolder(subDir.FullName);
            }

            FileInfo[] linkFiles = folderDir.GetFiles("*.lnk");

            foreach (FileInfo file in linkFiles)
            {
                string fileName = Path.GetFileNameWithoutExtension(file.Name);
                IWshShortcut shortCut = (IWshShortcut)myShell.CreateShortcut(file.FullName);

                BoxSharpSuggestItem suggest = new BoxSharpSuggestItem();
                suggest.ItemName = fileName;
                suggest.ItemGroup = "Application";
                suggest.ItemStarter = new System.Diagnostics.ProcessStartInfo();
                suggest.ItemStarter.Arguments = shortCut.Arguments;
                suggest.ItemStarter.FileName = shortCut.TargetPath;
                suggest.ItemStarter.WorkingDirectory = shortCut.WorkingDirectory;
                if (shortCut.Description != null && shortCut.Description.Length > 0)
                {
                    suggest.ItemDescription = shortCut.Description;
                }
                else
                {
                    suggest.ItemDescription = shortCut.TargetPath;
                }

                if (shortCut.TargetPath.Contains("{"))
                {
                    continue;
                }

                string[] fileNameWords = fileName.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);

                string nameForShort = "";
                bool shortNameComplete = false;

                foreach (string fileNameWord in fileNameWords)
                {
                    if (!shortNameComplete && (fileNameWord[0] < '0' || fileNameWord[0] > '9'))
                    {
                        nameForShort += fileNameWord[0];
                    }
                    else
                    {
                        shortNameComplete = true;
                    }

                    if (!suggests.ContainsKey(fileNameWord.ToLower()))
                    {
                        suggests.Add(fileNameWord.ToLower(), new List<BoxSharpSuggestItem>());
                    }

                    bool alreadyContain = false;
                    foreach (BoxSharpSuggestItem item in suggests[fileNameWord.ToLower()])
                    {
                        if (item.ItemStarter.Arguments == suggest.ItemStarter.Arguments && item.ItemStarter.FileName == suggest.ItemStarter.FileName && item.ItemStarter.WorkingDirectory == suggest.ItemStarter.WorkingDirectory)
                        {
                            alreadyContain = true;
                            break;
                        }
                    }

                    if (!alreadyContain)
                    {
                        suggests[fileNameWord.ToLower()].Add(suggest);
                    }
                }

                for (int j = 0; j < fileNameWords.Length; ++j)
                {
                    for (int k = j + 1; k < fileNameWords.Length; ++k)
                    {
                        string fileNameWord = "";
                        for (int s = j; s <= k; ++s)
                        {
                            fileNameWord += fileNameWords[s] + " ";
                        }

                        fileNameWord = fileNameWord.Substring(0, fileNameWord.Length - 1);

                        if (!suggests.ContainsKey(fileNameWord.ToLower()))
                        {
                            suggests.Add(fileNameWord.ToLower(), new List<BoxSharpSuggestItem>());
                        }

                        bool alreadyContain = false;
                        foreach (BoxSharpSuggestItem item in suggests[fileNameWord.ToLower()])
                        {
                            if (item.ItemStarter.Arguments == suggest.ItemStarter.Arguments && item.ItemStarter.FileName == suggest.ItemStarter.FileName && item.ItemStarter.WorkingDirectory == suggest.ItemStarter.WorkingDirectory)
                            {
                                alreadyContain = true;
                                break;
                            }
                        }

                        if (!alreadyContain)
                        {
                            suggests[fileNameWord.ToLower()].Add(suggest);
                        }
                    }
                }

                if (nameForShort.Length > 1)
                {
                    if (!suggests.ContainsKey(nameForShort.ToLower()))
                    {
                        suggests.Add(nameForShort.ToLower(), new List<BoxSharpSuggestItem>());
                    }

                    bool alreadyContain = false;
                    foreach (BoxSharpSuggestItem item in suggests[nameForShort.ToLower()])
                    {
                        if (item.ItemStarter.Arguments == suggest.ItemStarter.Arguments && item.ItemStarter.FileName == suggest.ItemStarter.FileName && item.ItemStarter.WorkingDirectory == suggest.ItemStarter.WorkingDirectory)
                        {
                            alreadyContain = true;
                            break;
                        }
                    }

                    if (!alreadyContain)
                    {
                        suggests[nameForShort.ToLower()].Add(suggest);
                    }
                }

                string exeName = Path.GetFileNameWithoutExtension(suggest.ItemStarter.FileName).ToLower();

                if (exeName.Length > 1)
                {
                    if (!suggests.ContainsKey(exeName.ToLower()))
                    {
                        suggests.Add(exeName.ToLower(), new List<BoxSharpSuggestItem>());
                    }

                    bool alreadyContain = false;
                    foreach (BoxSharpSuggestItem item in suggests[exeName.ToLower()])
                    {
                        if (item.ItemStarter.Arguments == suggest.ItemStarter.Arguments && item.ItemStarter.FileName == suggest.ItemStarter.FileName && item.ItemStarter.WorkingDirectory == suggest.ItemStarter.WorkingDirectory)
                        {
                            alreadyContain = true;
                            break;
                        }
                    }

                    if (!alreadyContain)
                    {
                        suggests[exeName.ToLower()].Add(suggest);
                    }
                }
            }
        }
    }
}
